﻿using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Linq.Expressions;

namespace YasharEl.Infrastructure.EntityModel.Validation
{
    /// <summary>
    /// Contract for the common entity validator.
    /// </summary>
    [ContractClass(typeof(EntityValidatorContract))]
    public interface IEntityValidator
    {
        ///<summary>Returns true if the entity is valid.</summary>
        ///<param name="entityInstance">The entity instance.</param>
        ///<returns>
        /// true if the <paramref name="entityInstance"/> 
        /// has, at least, one invalid value 
        /// </returns>
        bool IsValid<TEntity, TIdentity>(TEntity entityInstance) where TEntity : class, IGenericEntity<TIdentity>;

        ///<summary>
        /// Validates an entity and returns the information about invalid values.
        /// </summary>
        ///<param name="entityInstance">The entity instance.</param>
        ///<returns>The list of invalid values for the entity.</returns>
        IList<IInvalidValueInfo> Validate<TEntity, TIdentity>(TEntity entityInstance) where TEntity : class, IGenericEntity<TIdentity>;

        ///<summary>
        /// Validates a property of the entity and returns the information about invalid values.
        ///</summary>
        ///<param name="entityInstance">The entity instance.</param>
        ///<param name="property">The property. (getter)</param>
        ///<typeparam name="T">Type of the entity.</typeparam>
        ///<typeparam name="TP">Type of the property</typeparam>
        ///<returns>The list of invalid values for the given property.</returns>
        IList<IInvalidValueInfo> Validate<TEntity, TProperty, TIdentity>(TEntity entityInstance, Expression<Func<TEntity, TProperty>> property) where TEntity : class, IGenericEntity<TIdentity>;

        ///<summary>
        /// Validates a property of the entity and returns the information about invalid values.
        ///</summary>
        ///<param name="entityInstance">The entity instance.</param>
        ///<param name="propertyName">The name of one property of the <paramref name="entityInstance"/> </param>
        ///<returns>The list of invalid values for the given property.</returns>
        IList<IInvalidValueInfo> Validate<TEntity, TIdentity>(TEntity entityInstance, string propertyName) where TEntity : class, IGenericEntity<TIdentity>;
    }

    [ContractClassFor(typeof(IEntityValidator))]
    internal abstract class EntityValidatorContract : IEntityValidator
    {
        public bool IsValid<TEntity, TIdentity>(TEntity entityInstance) where TEntity : class, IGenericEntity<TIdentity>
        {
            Contract.Requires<ArgumentNullException>(entityInstance != null, "entityInstance could not be null.");
            return default(bool);
        }

        public IList<IInvalidValueInfo> Validate<TEntity, TIdentity>(TEntity entityInstance) where TEntity : class, IGenericEntity<TIdentity>
        {
            Contract.Requires<ArgumentNullException>(entityInstance != null, "entityInstance could not be null.");
            return new List<IInvalidValueInfo>();
        }

        public IList<IInvalidValueInfo> Validate<TEntity, TProperty, TIdentity>(TEntity entityInstance, Expression<Func<TEntity, TProperty>> property) where TEntity : class, IGenericEntity<TIdentity>
        {
            Contract.Requires<ArgumentNullException>(entityInstance != null, "entityInstance could not be null.");
            Contract.Requires<ArgumentNullException>(property != null, "property could not be null.");
            return new List<IInvalidValueInfo>();
        }

        public IList<IInvalidValueInfo> Validate<TEntity, TIdentity>(TEntity entityInstance, string propertyName) where TEntity : class, IGenericEntity<TIdentity>
        {
            Contract.Requires<ArgumentNullException>(entityInstance != null, "entityInstance could not be null.");
            Contract.Requires<ArgumentNullException>(!string.IsNullOrEmpty(propertyName), "propertyName could not be null or empty.");
            return new List<IInvalidValueInfo>();
        }
    }
}
